﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Profile;
using System.Web.Mvc;
using HQPad.Models;
using HQPad.DAL;
using HQPad.Services;
using HQPad.Helpers;

namespace HQPad.Controllers
{
    public class CharacterController : BaseController
    {
        private string userName = string.Empty;
        private string sessionId = string.Empty;

        //
        // GET: /Character/{name}

        /// <summary>
        /// Load the default view page for a character (skill list).
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult Index(string name)
        {
            BuildPageTitle("Edit the hero: {0}");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            string userName = AuthenticationHelper.GetCurrentUserName(HttpContext);
            string sessionId = AuthenticationHelper.GetCurrentSessionId(Request);

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.CharacterName = character.CharacterName;
            ViewBag.Silhouette = CreateSilhouetteModel(context, character);

            var profile = ProfileBase.Create(AuthenticationHelper.GetCurrentUserName(HttpContext));
            bool showAllSkills = (profile.GetPropertyValue("ShowAllGuildSkills") == null) ? true : Convert.ToBoolean(profile.GetPropertyValue("ShowAllGuildSkills"));

            // Get available skills + any extras bought (but not necessarily available) to display in skills list.
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            IEnumerable<IGrouping<string, SkillItem>> skills = xHelper.GetSkills(showAllSkills).OrderBy(g => g.Name).GroupBy(g => g.Group);

            return View(skills);
        }

        public ActionResult Battleboard(string name)
        {
            BuildPageTitle("Battleboard for {0}");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (character == null)
                return RedirectToAction("NotFound");

            BattleboardPageModel model = new BattleboardPageModel()
            {
                CharacterName = name,
                HasTalisman = xHelper.HasTalisman(),
                TotalPower = xHelper.GetTotalPower()
            };

            return View(model);
        }

        #region New
        /// <summary>
        /// Service method to create a new blank character on the scratch pad.
        /// </summary>
        /// <returns></returns>
        public ActionResult New()
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // How many unsaved characters on scratch pad?
            IEnumerable<ScratchPad> scratchCharacters = characterService.ListScratchPadCharacters(userName, sessionId);

            int unsaved = (from c in scratchCharacters
                           where !c.CharacterID.HasValue && c.CharacterName.StartsWith("mystery hero")
                           select c).Count();

            // Create a new blank character.
            ScratchPad character = characterService.CreateScratchPadCharacter(userName, sessionId);

            if (unsaved > 0) // update name with index if necessary.
            {
                character.CharacterName = string.Format("{0} ({1})", character.CharacterName, unsaved);
                character.Slug = characterService.CreateCharacterSlug(character.CharacterName);
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            xHelper.SetName(character.CharacterName);

            xHelper.SaveToCharacter(character);

            // validate character.
            bool invalid = false;

            foreach (var validationResults in ((HQContext)context).GetValidationErrors())
            {
                foreach (var error in validationResults.ValidationErrors)
                {
                    invalid = true;
                    //Debug.WriteLine("Entity Property: {0}, Error {1}",
                    //                  error.PropertyName,error.ErrorMessage);
                }
            }

            if (!invalid)
            {
                // save changes.
                context.Save();
                characterContext.Save();

                return RedirectToAction("Index", new { name = characterService.CreateCharacterSlug(character) });
            }
            else
                return RedirectToAction("Dashboard", "Home");
        }
        #endregion

        public ActionResult NotFound()
        {
            BuildPageTitle("Hero is missing in action");
            return View();
        }

        #region Record
        /// <summary>
        /// Show the adventure record list page.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult Record(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            BuildPageTitle(string.Format("Adventure record for {0}", character.CharacterName));

            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.Record = CreateAdventureRecordModel(context, character);
            ViewBag.CharacterName = character.CharacterName;

            return View();
        }
        #endregion

        #region EditRecord
        /// <summary>
        /// Show the edit adventure record modal window.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult EditRecord(string name, int id)
        {
            BuildPageTitle("Edit adventure record");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            RecordItem record = xHelper.GetAdventureRecords()
                .Where(r => r.ID == id)
                .FirstOrDefault();

            if (record == null)
            {
                // gracefully handle the missing record?
            }

            return View(record);
        }
        #endregion

        #region DeleteScratch
        /// <summary>
        /// Delete a scratch pad character.
        /// Redirects to the dashboard.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult DeleteScratch(string name)
        {
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            characterService.DeleteScratchPadCharacter(character.ScratchPadID);

            characterContext.Save();

            return RedirectToAction("Dashboard", "Home");
        }
        #endregion

        #region Magic
        /// <summary>
        /// Load the view page for a characters magic spells, spell book and school access.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult Magic(string name)
        {
            BuildPageTitle("Edit the hero: {0} - Magic");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            SchoolAccessModel schoolModel = CreateSchoolAccessModel(context, xHelper);
            ViewBag.Schools = schoolModel;

            ViewBag.Spells = CreateSpellModel(context, xHelper);

            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.CharacterName = character.CharacterName;

            // Get available skills + any extras bought (but not necessarily available) to display in skills list.

            //IEnumerable<IGrouping<string, SkillItem>> skills = xHelper.GetSkills().OrderBy(g => g.Name).GroupBy(g => g.Group);

            return View();
        }

        [HttpPost]
        public ActionResult Magic(string name, FormCollection form, string setSchools, string setStatus)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper helper = new CharacterRulesHelper(context, character.Xml);

            SchoolAccessModel model = CreateSchoolAccessModel(context, helper);

            if (!string.IsNullOrEmpty(setSchools))
            {
                // Loop each access item (which was used to build the form) and extract from the form collection
                // selected schools.  This will get handed back to the character helper for writing to the xml.
                foreach (SchoolAccessModelItem accessItem in model.AccessItems)
                {
                    string fieldName = string.Format("access_{0}", accessItem.AccessSlug);

                    // get form value from relevant drop down list.
                    string value = form[fieldName];

                    if (string.IsNullOrEmpty(value))
                    {
                        ModelState.AddModelError(fieldName, string.Format("You must choose a school of magic for the {0}.", accessItem.AccessName.ToLowerInvariant()));
                        continue;
                    }

                    accessItem.SchoolSlug = value;
                }

                if (ModelState.IsValid)
                {
                    // Save selected schools back to character.
                    helper.SaveSchoolOptions(model.AccessItems);

                    helper.SaveToCharacter(character); // save back xml changes.

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save data.
                    //context.Save();
                    characterContext.Save();

                    // update school access record.
                    model.Records.Clear();
                    model.Records.AddRange(helper.GetSchoolAccess());
                }
            }

            if (!string.IsNullOrEmpty(setStatus))
            {
                //int total1to8 = 0;
                //int total9 = 0;
                //int total10 = 0;

                //// Get status groups and loop to check for status assignments.
                //foreach (LearntS item in model.Status)
                //{
                //    string fieldName = string.Format("status_{0}_{1}", item.SchoolSlug, Enum.GetName(typeof(SchoolStatusAssignmentType), item.Type));

                //    // get form value from relevant drop down list.
                //    string value = form[fieldName];

                //    // convert allocation to int.
                //    int allocation = 0;

                //    if (!string.IsNullOrEmpty(value))
                //    {
                //        if (!int.TryParse(value, out allocation))
                //            ModelState.AddModelError(fieldName, "Not a valid number.");
                //    }

                //    if (item.Type == SchoolStatusAssignmentType.Level1to8)
                //        total1to8 += allocation;
                //    else if (item.Type == SchoolStatusAssignmentType.Level9)
                //        total9 += allocation;
                //    else
                //        total10 += allocation;

                //    // update model data.
                //    item.Status = allocation;
                //}

                //// Check total's not been passed.
                //if (total10 > model.Status.Totals.Where(a => a.Type == SchoolStatusAssignmentType.Level10).Sum(b => b.Total))
                //    ModelState.AddModelError("generalError", "You have assigned more status than available to level 10 spells.");

                //if (total9 > model.Status.Totals.Where(a => a.Type == SchoolStatusAssignmentType.Level9).Sum(b => b.Total))
                //    ModelState.AddModelError("generalError", "You have assigned more status than available to level 9 spells.");

                //if (total1to8 > model.Status.Totals.Where(a => a.Type == SchoolStatusAssignmentType.Level1to8).Sum(b => b.Total))
                //    ModelState.AddModelError("generalError", "You have assigned more status than available to level 1-8 spells.");

                //if (ModelState.IsValid)
                //{
                //    // Save data
                //    helper.SaveSchoolStatus(model.Status);

                //    helper.SaveToCharacter(character); // save back xml changes.

                //    characterService.UpdateScratchPadCharacter(character); // set to modified.

                //    // Save data.
                //    context.Save();

                //    model.Status = helper.GetSchoolStatus();
                //}
            }

            // Load page required values.
            ViewBag.Schools = model;
            ViewBag.Spells = CreateSpellModel(context, helper);
            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.CharacterName = character.CharacterName;

            return View();
        }
        #endregion

        #region Power
        /// <summary>
        /// Load the view page for a characters invocations and sect access.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult Power(string name)
        {
            BuildPageTitle("Edit the hero: {0} - Power");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            SectAccessModel sectModel = CreateSectAccessModel(context, xHelper);
            ViewBag.Sects = sectModel;

            ViewBag.Invocations = CreateInvocationModel(context, xHelper);

            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.CharacterName = character.CharacterName;

            return View();
        }

        [HttpPost]
        public ActionResult Power (string name, FormCollection form, string setStatus)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper helper = new CharacterRulesHelper(context, character.Xml);

            SectAccessModel model = CreateSectAccessModel(context, helper);

            if (!string.IsNullOrEmpty(setStatus))
            {
                int total1to8 = 0;
                int total9 = 0;
                int total10 = 0;

                // Get status groups and loop to check for status assignments.
                foreach (SectStatusAssignment item in model.Status.Status)
                {
                    string fieldName = string.Format("status_{0}_{1}", item.SectSlug, Enum.GetName(typeof(SectStatusAssignmentType), item.Type));

                    // get form value from relevant drop down list.
                    string value = form[fieldName];

                    // convert allocation to int.
                    int allocation = 0;

                    if (!string.IsNullOrEmpty(value))
                    {
                        if (!int.TryParse(value, out allocation))
                            ModelState.AddModelError(fieldName, "Not a valid number.");
                    }

                    if (item.Type == SectStatusAssignmentType.Level1to8)
                        total1to8 += allocation;
                    else if (item.Type == SectStatusAssignmentType.Level9)
                        total9 += allocation;
                    else
                        total10 += allocation;

                    // update model data.
                    item.Status = allocation;
                }

                // Check total's not been passed.
                if (total10 > model.Status.Totals.Where(a => a.Type == SectStatusAssignmentType.Level10).Sum(b => b.Total))
                    ModelState.AddModelError("generalError", "You have assigned more status than available to level 10 spells.");

                if (total9 > model.Status.Totals.Where(a => a.Type == SectStatusAssignmentType.Level9).Sum(b => b.Total))
                    ModelState.AddModelError("generalError", "You have assigned more status than available to level 9 spells.");

                if (total1to8 > model.Status.Totals.Where(a => a.Type == SectStatusAssignmentType.Level1to8).Sum(b => b.Total))
                    ModelState.AddModelError("generalError", "You have assigned more status than available to level 1-8 spells.");

                if (ModelState.IsValid)
                {
                    // Save data
                    helper.SaveSectStatus(model.Status);

                    helper.SaveToCharacter(character); // save back xml changes.

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save data.
                    characterContext.Save();

                    model.Status = helper.GetSectStatus();
                }
            }

            ViewBag.Sects = model; 
            ViewBag.Invocations = CreateInvocationModel(context, helper);
            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.CharacterName = character.CharacterName;

            return View();
        }
        #endregion

        #region Armour
        public ActionResult Armour(string name)
        {
            BuildPageTitle("Edit the hero: {0} - Armour");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            ViewBag.CharacterName = character.CharacterName;
            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.Silhouette = CreateSilhouetteModel(context, character);

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            List<ArmourItem> armours = xHelper.GetAllArmour();

            return View(armours);
        }
        #endregion

        #region EditArmour
        /// <summary>
        /// The edit armour modal window.
        /// </summary>
        /// <param name="name">A character name.</param>
        /// <param name="id">An optional armour item id.  Null indicates we are adding a new armour item.</param>
        /// <returns></returns>
        public ActionResult EditArmour(string name, int? id)
        {
            BuildPageTitle("Edit the hero: {0} - Add/edit armour");

            ArmourItem model = null;

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (id.HasValue)
            {
                List<ArmourItem> armours = xHelper.GetAllArmour();

                model = armours.Where(a => a.Id == id.Value).FirstOrDefault();
            }

            ViewBag.ArmourTypes = xHelper.GetArmourTypeList();

            return View(model);
        }
        #endregion

        #region EditQualities
        /// <summary>
        /// The edit item qualities modal window.
        /// </summary>
        /// <param name="name">A character name.</param>
        /// <param name="id">An optional armour item id.  Null indicates we are adding a new armour item.</param>
        /// <returns></returns>
        public ActionResult EditQualities(string name, int? id)
        {
            BuildPageTitle("Edit the hero: {0} - Add/edit item qualities");

            IQualityContainer model = null;

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (id.HasValue)
            {
                model = xHelper.GetQualityContainer(id.Value);
            }

            ViewBag.QualityTypes = xHelper.GetQualityTypeList();
            ViewBag.SkillList = CreateSkillList(context);
            ViewBag.SectList = CreateSectList(context);
            ViewBag.SchoolList = CreateSchoolList(context);

            return View(model);
        }
        #endregion

        #region RemoveQuality
        /// <summary>
        /// Remove a quality from an item.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="qualityId"></param>
        /// <param name="itemId"></param>
        /// <returns></returns>
        public ActionResult RemoveQuality(string name, int id, int itemId)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            // remove the quality.
            try
            {
               xHelper.RemoveItemQuality(itemId, id);

               xHelper.SaveToCharacter(character); // save back xml changes.

               characterService.UpdateScratchPadCharacter(character); // set to modified.

               // Save changes to db.
               characterContext.Save();
            }
            catch (Exception exception)
            {
               Response.StatusCode = 500;
               return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return RedirectToAction("EditQualities", new { name = name, id = itemId });
        }
        #endregion

        #region AddQuality
        /// <summary>
        /// Add a new quality to an item.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="form"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult AddQuality(string name, FormCollection form)
        {
            string itemIdString = form["itemId"];
            string typeString = form["bonusType"];
            string quantityString = form["quantity"];
            string value = string.Empty;

            int itemId = 0;
            BonusTypes type = BonusTypes.LifeRank;
            int quantity = 1;

            if (!int.TryParse(itemIdString, out itemId))
                throw new ApplicationException("Invalid item id value.");

            if (!Enum.TryParse<BonusTypes>(typeString, out type))
                throw new ApplicationException("Invalid bonus type value.");

            if (!int.TryParse(quantityString, out quantity))
                throw new ApplicationException("Invalid quantity value.");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            // determine which value (if any to use)
            List<Quality> qualities = xHelper.GetQualityTypeList();

            Quality quality = (from a in qualities
                              where a.Type == type
                              select a).FirstOrDefault();

            switch (quality.Choice)
            {
                case QualityChoiceType.SkillName:
                {
                    value = form["skillSlug"];
                    break;
                }
                case QualityChoiceType.School:
                {
                    value = form["schoolSlug"];
                    break;
                }
                case QualityChoiceType.Sect:
                {
                    value = form["sectSlug"];
                    break;
                }
                case QualityChoiceType.SpellLevel:
                {
                    value = form["spellLevel"];
                    break;
                }
                default:
                {
                    value = string.Empty;
                    break;
                }
            }

            try
            {
                xHelper.AddQuality(itemId, type, quantity, value); // add quality to item.

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return RedirectToAction("EditQualities", new { name = name, id = itemId });
        }
        #endregion

        #region Items
        public ActionResult Items(string name)
        {
            BuildPageTitle("Edit the hero: {0} - Items and Abilities");

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            ViewBag.CharacterName = character.CharacterName;
            ViewBag.Header = CreateHeaderModel(context, character);
            ViewBag.Silhouette = CreateSilhouetteModel(context, character);

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            List<ItemBase> items = xHelper.GetItems();

            return View(items);
        }
        #endregion

        #region EditItem
        /// <summary>
        /// The edit item modal window.
        /// </summary>
        /// <param name="name">A character name.</param>
        /// <param name="id">An optional item id.  Null indicates we are adding a new item.</param>
        /// <returns></returns>
        public ActionResult EditItem(string name, int? id)
        {
            BuildPageTitle("Edit the hero: {0} - Add/edit item");

            Item model = null;

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (id.HasValue)
            {
                List<ItemBase> items = xHelper.GetItems();

                model = (Item) items.Where(a => a.Id == id.Value).FirstOrDefault();
            }

            ViewBag.UseTypes = xHelper.GetItemUseTypeList();
            ViewBag.FieldName = "itemIcon";
            ViewBag.ContainerName = "item-icon-container";
            ViewBag.IconList = CreateIconList();

            return View(model);
        }
        #endregion

        #region EditAbility
        /// <summary>
        /// The edit ability modal window.
        /// </summary>
        /// <param name="name">A character name.</param>
        /// <param name="id">An optional item id.  Null indicates we are adding a new item.</param>
        /// <returns></returns>
        public ActionResult EditAbility(string name, int? id)
        {
            BuildPageTitle("Edit the hero: {0} - Add/edit ability");

            Ability model = null;

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (id.HasValue)
            {
                List<ItemBase> abilities = xHelper.GetItems();

                model = (Ability)abilities.Where(a => a.Id == id.Value).FirstOrDefault();
            }

            ViewBag.UseTypes = xHelper.GetItemUseTypeList();
            ViewBag.FieldName = "itemIcon";
            ViewBag.ContainerName = "item-icon-container";
            ViewBag.IconList = CreateIconList();

            return View(model);
        }
        #endregion

        #region EditScroll
        /// <summary>
        /// The edit scroll modal window.
        /// </summary>
        /// <param name="name">A character name.</param>
        /// <param name="id">An optional item id.  Null indicates we are adding a new item.</param>
        /// <returns></returns>
        public ActionResult EditScroll(string name, int? id)
        {
            BuildPageTitle("Edit the hero: {0} - Add/edit scroll");

            Item model = null;

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
                return RedirectToAction("NotFound");

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            //if (id.HasValue)
            //{
            //    List<ItemBase> items = xHelper.GetItems();

            //    model = (Item)items.Where(a => a.Id == id.Value).FirstOrDefault();
            //}

            //ViewBag.UseTypes = xHelper.GetItemUseTypeList();
            //ViewBag.FieldName = "itemIcon";
            //ViewBag.ContainerName = "item-icon-container";
            //ViewBag.IconList = CreateIconList();

            return View(model);
        }
        #endregion

        #region Service Methods

        #region RefreshRecord
        /// <summary>
        /// Service method to return the latest character page record list html.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshRecord(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Record list could not be update because character was invalid.");
            }

            List<RecordItem> records = CreateAdventureRecordModel(context, character);

            return PartialView("CharacterRecord", records);
        }
        #endregion

        #region RefreshHeader
        /// <summary>
        /// Service method to return the latest character page header html.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshHeader(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Header could not be update because character was invalid.");
            }

            CharacterHeader header = CreateHeaderModel(context, character);

            return PartialView("CharacterHeader", header);
        }
        #endregion

        #region SetRace
        /// <summary>
        /// Service method to update the race of a character.
        /// </summary>
        /// <param name="name">The character name slug.</param>
        /// <returns>The character header partial view.</returns>
        [HttpGet]
        public ActionResult SetRace(string name)
        {
            string race = Request.QueryString["race"];
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (!string.IsNullOrEmpty(race))
            {
                if (character != null)
                {
                    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

                    // Set new race. Recalculates all points values and totals.
                    xHelper.SetRace(race);

                    xHelper.SaveToCharacter(character); // save back xml changes.

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save changes to db.
                    characterContext.Save();
                }
            }

            CharacterHeader headerModel = CreateHeaderModel(context, character);

            return PartialView("CharacterHeader", headerModel);
        }
        #endregion

        #region SetClass
        /// <summary>
        /// Service method to set the class of a character.
        /// </summary>
        /// <param name="name">The character name slug.</param>
        /// <returns>The character header partial view.</returns>
        public ActionResult SetClass(string name)
        {
            string characterClass = Request.QueryString["class"];
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (!string.IsNullOrEmpty(characterClass))
            {
                if (character != null)
                {
                    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

                    // Set new race. Recalculates all points values and totals.
                    xHelper.SetClass(characterClass);

                    xHelper.SaveToCharacter(character); // save back xml changes.

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save changes to db.
                    characterContext.Save();
                }
            }

            CharacterHeader headerModel = CreateHeaderModel(context, character);

            return PartialView("CharacterHeader", headerModel);
        }
        #endregion

        #region SetGuild
        /// <summary>
        /// Service method to set the guild of a character.
        /// </summary>
        /// <param name="name">character name slug.</param>
        /// <returns></returns>
        public ActionResult SetGuild(string name)
        {
            string guildIdString = Request.QueryString["guildId"];
            string guildType = Request.QueryString["type"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);
            IRulesService rulesService = new RulesService(context);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (!string.IsNullOrEmpty(guildIdString) && !string.IsNullOrEmpty(guildType))
            {
                if (character != null)
                {
                    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

                    try
                    {
                        // Set new guild.
                        int guildId = 0;

                        if (!int.TryParse(guildIdString, out guildId))
                        {
                            Response.StatusCode = 500;
                            return PartialView("Message", "An invalid guild id was passed.");
                        }

                        bool success = false;
                        string reason = "failed to assign guild.";

                        if (guildId == 0) // remove the guild.
                        {
                            xHelper.RemoveGuild(guildType);
                            success = true;
                        }
                        else
                        {
                            IGuildService guildService = new GuildService(context);
                            Guild guild = guildService.GetGuild(guildId);

                            if (guild != null)
                                success = xHelper.SetGuild(guild, guildType, out reason);
                        }

                        if (success)
                        {
                            xHelper.SaveToCharacter(character); // save back xml changes.

                            characterService.UpdateScratchPadCharacter(character); // set to modified.

                            // Save changes to db.
                            characterContext.Save();
                        }
                        else
                        {
                            Response.StatusCode = 500;
                            return PartialView("Message", reason);
                        }

                    }
                    catch (Exception exception)
                    {
                        Response.StatusCode = 500;
                        return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
                    }
                }
            }

            CharacterHeader headerModel = CreateHeaderModel(context, character);

            return PartialView("CharacterHeader", headerModel);
        }
        #endregion

        #region AddRecord
        /// <summary>
        /// Service method to add a new adventure record to a character.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        [HttpGet]
        public ActionResult AddRecord(string name)
        {
            string eventDate = Request.QueryString["date"];
            string eventName = Request.QueryString["name"];
            string eventpoints = Request.QueryString["points"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);
            IRulesService rulesService = new RulesService(context);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }
            DateTime date = DateTime.Now;

            if (eventDate == null || string.IsNullOrEmpty(eventDate) || !DateTime.TryParse(eventDate, out date))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The event date is invalid.");
            }

            if (string.IsNullOrEmpty(eventName))
                eventName = string.Empty;

            int points = 0;

            if (eventpoints == null || string.IsNullOrEmpty(eventpoints) || !int.TryParse(eventpoints, out points))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The points value is invalid.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                // todo: add deaths, attendance type and event type to record forms.
                xHelper.AddAdventureRecord(date, eventName, points, null, AttendanceType.Unknown, null);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            List<RecordItem> records = CreateAdventureRecordModel(context, character);
            return PartialView("CharacterRecord", records);
        }

        [HttpPost]
        public ActionResult AddRecord(string name, FormCollection formCollection)
        {
            return PartialView("Message", "Not implemented.");
        }
        #endregion

        #region UpdateName
        /// <summary>
        /// Service method to set the name of a character.
        /// </summary>
        /// <param name="name">The character name slug.</param>
        /// <returns>The character header partial view.</returns>
        public JsonResult UpdateName(string name)
        {
            string characterName = Request.QueryString["name"];
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
            {
                JsonResponse error = new JsonResponse()
                {
                    Status = JsonStatus.Error,
                    Data = "Bad authentication or sessions details."
                };

                return Json(error, JsonRequestBehavior.AllowGet);
            }
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (!string.IsNullOrEmpty(characterName.Trim()))
            {
                if (character != null)
                {
                    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

                    // Set new name.
                    xHelper.SetName(characterName.Trim());

                    xHelper.SaveToCharacter(character); // save back xml changes.

                    character.CharacterName = characterName.Trim();
                    character.Slug = characterService.CreateCharacterSlug(characterName);

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save changes to db.
                    characterContext.Save();
                }
            }

            JsonResponse response = new JsonResponse()
            {
                Status = JsonStatus.Ok,
                Data = string.Format("http://{0}/character/{1}", Request.Url.Host, character.Slug)
            };

            return Json(response, JsonRequestBehavior.AllowGet);
        }
        #endregion

        #region SaveCharacter
        /// <summary>
        /// Service method to save a character to permanent storage.
        /// </summary>
        /// <param name="name">The character name slug.</param>
        /// <returns>The character header partial view.</returns>
        public ActionResult SaveCharacter(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            try
            {

                ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

                if (character != null)
                {
                    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

                    // save to permanent storage.
                    Character savedCharacter = characterService.SaveScratchPadAsCharacter(character.ScratchPadID);

                    // Save changes to db.
                    characterContext.Save();

                    // If necessary update the scratch pad character with the new character id so that
                    // we don't continually create new characters when we save.
                    if (!character.CharacterID.HasValue)
                    {
                        character.CharacterID = savedCharacter.CharacterID;
                        characterContext.Save();
                    }
                }
            }
            catch (Exception ex)
            {
                return PartialView("Message", string.Format("Error saving character: {0}", ex.Message));
            }

            return PartialView("Message", "Character saved for later.");
        }
        #endregion

        #region UpdateRecord
        /// <summary>
        /// Service method to update an adventure record.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult UpdateRecord(string name, int? id)
        {
            if (!id.HasValue)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The record id is invalid.");
            }

            string eventDate = Request.QueryString["date"];
            string eventName = Request.QueryString["name"];
            string eventpoints = Request.QueryString["points"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }
            DateTime date = DateTime.Now;

            if (eventDate == null || string.IsNullOrEmpty(eventDate) || !DateTime.TryParse(eventDate, out date))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The event date is invalid.");
            }

            if (string.IsNullOrEmpty(eventName))
                eventName = string.Empty;

            int points = 0;

            if (eventpoints == null || string.IsNullOrEmpty(eventpoints) || !int.TryParse(eventpoints, out points))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The points value is invalid.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.UpdateAdventureRecord(id.Value, date, eventName, points);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Adventure record updated.");
        }
        #endregion

        #region RemoveRecord
        /// <summary>
        /// Service method to remove an adveture record by id.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult RemoveRecord(string name, int? id)
        {
            if (!id.HasValue)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The record id is invalid.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.RemoveAdventureRecord(id.Value);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Adventure record removed.");
        }
        #endregion

        #region RefreshSkills
        /// <summary>
        /// Service method to refresh the list of character skills
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshSkills(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            var profile = ProfileBase.Create(AuthenticationHelper.GetCurrentUserName(HttpContext));
            bool showAllSkills = (profile.GetPropertyValue("ShowAllGuildSkills") == null) ? true : Convert.ToBoolean(profile.GetPropertyValue("ShowAllGuildSkills"));

            // Get available skills + any extras bought (but not necessarily available) to display in skills list.
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            IEnumerable<IGrouping<string, SkillItem>> skills = xHelper.GetSkills(showAllSkills).OrderBy(g => g.Name).GroupBy(g => g.Group);

            return PartialView("CharacterSkills", skills);
        }
        #endregion

        #region RefreshSilhouette
        /// <summary>
        /// Service method to refresh the character silhouette control view.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshSilhouette(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            SilhouetteRecord model = CreateSilhouetteModel(context, character);

            return PartialView("CharacterSilhouette", model);
        }
        #endregion

        #region UpdateSkill
        ///// <summary>
        ///// A service method to update the number of ranks for a skill.
        ///// </summary>
        ///// <param name="name">The current hero name slug.</param>
        ///// <returns>A sppropriate message.</returns>
        //public JsonResult UpdateSkill(string name)
        //{
        //    string skillIdString = Request.QueryString["skillid"];
        //    string ranksString = Request.QueryString["ranks"];
        //    JsonResponse response;

        //    if (string.IsNullOrEmpty(skillIdString))
        //    {
        //        response = new JsonResponse()
        //        {
        //            Status = JsonStatus.Error,
        //            Message = "Invalid skill id parameter."
        //        };
        //        return Json(response);
        //    }

        //    int ranks = 0;

        //    if (string.IsNullOrEmpty(ranksString) || !int.TryParse(ranksString, out ranks))
        //    {
        //        response = new JsonResponse()
        //        {
        //            Status = JsonStatus.Error,
        //            Message = "Invalid ranks parameter."
        //        };
        //        return Json(response); 
        //    }

        //    IUnitOfWork context = new HQContext();

        //    ICharacterService characterService = new CharacterService(context);

        //    string userName = AuthenticationHelper.GetCurrentUserName(HttpContext);
        //    string sessionId = AuthenticationHelper.GetCurrentSessionId(Request);

        //    ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

        //    if (character == null)
        //    {
        //        response = new JsonResponse()
        //        {
        //            Status = JsonStatus.Error,
        //            Message = "Error locating character record from scratch pad."
        //        };
        //        return Json(response);
        //    }

        //    CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);
        //    List<SkillItemChange> changes;

        //    try
        //    {
        //        string reason = string.Empty;
        //        bool success = xHelper.UpdateSkill(skillIdString, ranks, out reason, out changes);

        //        if (success)
        //        {
        //            xHelper.SaveToCharacter(character); // save back xml changes.

        //            characterService.UpdateScratchPadCharacter(character); // set to modified.

        //            // Save changes to db.
        //            context.Save();
        //        }
        //        else
        //        {
        //            response = new JsonResponse()
        //            {
        //                Status = JsonStatus.Error,
        //                Message = reason
        //            };
        //            return Json(response);
        //        }
        //    }
        //    catch (Exception exception)
        //    {
        //        Response.StatusCode = 500;
        //        response = new JsonResponse()
        //        {
        //            Status = JsonStatus.Error,
        //            Message = string.Format("A system error occured. {0}", exception.Message)
        //        };
        //        return Json(response);
        //    }

        //    response = new JsonResponse()
        //    {
        //        Status = JsonStatus.Ok,
        //        Message = "ok",
        //        Data = changes
        //    };

        //    return Json(response, JsonRequestBehavior.AllowGet);
        //}

        /// <summary>
        /// A service method to update the number of ranks for a skill.
        /// </summary>
        /// <param name="name">The current hero name slug.</param>
        /// <returns>A sppropriate message.</returns>
        public ActionResult UpdateSkill(string name)
        {
            string skillIdString = Request.QueryString["skillid"];
            string ranksString = Request.QueryString["ranks"];

            if (string.IsNullOrEmpty(skillIdString))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid skill id parameter.");
            }

            int ranks = 0;

            if (string.IsNullOrEmpty(ranksString) || !int.TryParse(ranksString, out ranks))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid ranks parameter.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Error locating character record from scratch pad.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                string reason = string.Empty;
                bool success = xHelper.UpdateSkill(skillIdString, ranks, out reason);

                if (success)
                {
                    xHelper.SaveToCharacter(character); // save back xml changes.

                    characterService.UpdateScratchPadCharacter(character); // set to modified.

                    // Save changes to db.
                    characterContext.Save();
                }
                else
                    return PartialView("Message", reason);
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            var profile = ProfileBase.Create(AuthenticationHelper.GetCurrentUserName(HttpContext));
            bool showAllSkills = (profile.GetPropertyValue("ShowAllGuildSkills") == null) ? true : Convert.ToBoolean(profile.GetPropertyValue("ShowAllGuildSkills"));

            IEnumerable<IGrouping<string, SkillItem>> skills = xHelper.GetSkills(showAllSkills).OrderBy(g => g.Name).GroupBy(g => g.Group);

            return PartialView("CharacterSkills", skills);

//            return PartialView("Message", "Skill updated.");
        }
        #endregion

        #region UpdateSpell
        public ActionResult UpdateSpell(string name)
        {
            string groupname = Request.QueryString["group"];
            string selectedString = Request.QueryString["selected"];
            string attunedString = Request.QueryString["attuned"];
            bool selected = false;
            bool attuned = false;

            if (string.IsNullOrEmpty(groupname))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid spell group name parameter.");
            }

            if (string.IsNullOrEmpty(selectedString) || !bool.TryParse(selectedString, out selected))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid selected parameter.");
            }

            if (string.IsNullOrEmpty(attunedString) || !bool.TryParse(attunedString, out attuned))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid attuned parameter.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Error locating character record from scratch pad.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            xHelper.UpdateSpell(groupname, selected, attuned);

            xHelper.SaveToCharacter(character); // save back xml changes.

            characterService.UpdateScratchPadCharacter(character); // set to modified.

            // Save changes to db.
            characterContext.Save();

            SpellListModel spells = CreateSpellModel(context, xHelper);

            return PartialView("CharacterSpells", spells);
        }
        #endregion

        #region UpdateInvocation
        public ActionResult UpdateInvocation(string name)
        {
            string groupname = Request.QueryString["group"];
            string selectedString = Request.QueryString["selected"];
            bool selected = false;

            if (string.IsNullOrEmpty(groupname))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid invocation group name parameter.");
            }

            if (string.IsNullOrEmpty(selectedString) || !bool.TryParse(selectedString, out selected))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Invalid selected parameter.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Error locating character record from scratch pad.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            // check the invocation choice is valid.
            if (selected && !xHelper.CanLearnInvocation(groupname))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The invocation cannot be learnt due to a missing requirement.");
            }

            xHelper.UpdateInvocation(groupname, selected);

            xHelper.SaveToCharacter(character); // save back xml changes.

            characterService.UpdateScratchPadCharacter(character); // set to modified.

            // Save changes to db.
            characterContext.Save();

            InvocationListModel invocations = CreateInvocationModel(context, xHelper);

            return PartialView("CharacterInvocations", invocations);
        }
        #endregion

        #region UpdateArmour
        /// <summary>
        /// Service method to update an adventure record.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult UpdateArmour(string name, int? id)
        {
            //if (!id.HasValue)
            //{
            //    Response.StatusCode = 500;
            //    return PartialView("Message", "The record id is invalid.");
            //}

            string armourType = Request.QueryString["type"];
            string armourName = Request.QueryString["name"];
            string armourHqid = Request.QueryString["hqid"];
            string armourId = Request.QueryString["armourId"];
            string armourSuperior = Request.QueryString["superior"];
            string armourEmpowered = Request.QueryString["empowered"];
            string armourEnsorcelled = Request.QueryString["ensorcelled"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            if (string.IsNullOrEmpty(armourType))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The armour type is invalid.");
            }

            int superior = 0;

            if (!string.IsNullOrEmpty(armourSuperior) && !int.TryParse(armourSuperior, out superior))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The superior value is invalid.");
            }
            
            int empowered = 0;

            if (!string.IsNullOrEmpty(armourEmpowered) && !int.TryParse(armourEmpowered, out empowered))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The empowered value is invalid.");
            }

            int ensorcelled = 0;

            if (!string.IsNullOrEmpty(armourEnsorcelled) && !int.TryParse(armourEnsorcelled, out ensorcelled))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The ensorcelled value is invalid.");
            }

            ArmourType type = ArmourType.Metal;

            if (!Enum.TryParse(armourType, out type))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The armour type is invalid.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.UpdateArmour(id, armourName, type, superior, empowered, ensorcelled, armourHqid);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Armour updated.");
        }
        #endregion

        #region RemoveItem
        /// <summary>
        /// Service method to remove an item or armour by id.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult RemoveItem(string name, int? id)
        {
            if (!id.HasValue)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item/armour id is invalid.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.RemoveItem(id.Value);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Item/armour removed.");
        }
        #endregion

        #region RefreshArmour
        /// <summary>
        /// Service method to return the latest armour list html.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshArmour(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Record list could not be update because character was invalid.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);
            List<ArmourItem> armours = xHelper.GetAllArmour();

            return PartialView("CharacterArmours", armours);
        }
        #endregion

        #region EquipItem
        /// <summary>
        /// Service method to equip (or un equip) an item or armour by id.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult EquipItem(string name, int? id)
        {
            if (!id.HasValue)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item/armour id is invalid.");
            }

            string equipString = Request.QueryString["equipped"];
            bool equipped = false;

            if (string.IsNullOrEmpty(equipString) || !bool.TryParse(equipString, out equipped))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The equipped value is missing or not a stringified boolean.");
            }

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.ItemEquipped(id.Value, equipped);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", string.Format("Item/armour {0}.", equipped ? "equipped" : "unequipped"));
        }
        #endregion

        #region UpdateItem
        /// <summary>
        /// Service method to update an item record.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult UpdateItem(string name, int? id)
        {
            string itemUseType = Request.QueryString["usetype"];
            string itemName = Request.QueryString["itemname"];
            string itemHqid = Request.QueryString["hqid"];
            string itemDescription = Request.QueryString["description"];
            string itemCharges = Request.QueryString["charges"];
            string itemFrequency = Request.QueryString["frequency"];
            string itemIcon = Request.QueryString["icon"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            if (string.IsNullOrEmpty(itemUseType))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item use type is invalid.");
            }

            ItemUseType type = ItemUseType.OnceEver;

            if (!Enum.TryParse(itemUseType, out type))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item type is invalid.");
            }

            if (!string.IsNullOrEmpty(itemCharges))
            {
                int charges = 1;
                if (!int.TryParse(itemCharges, out charges))
                {
                    Response.StatusCode = 500;
                    return PartialView("Message", "The charges value must be a number.");
                }
            }
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.UpdateItem(id, type, itemHqid, itemName, itemDescription, itemCharges, itemFrequency, itemIcon);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Item updated.");
        }
        #endregion

        #region RefreshItem
        /// <summary>
        /// Service method to return the latest item list html.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshItem(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Record list could not be update because character was invalid.");
            }

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);
            List<ItemBase> items = xHelper.GetItems();

            return PartialView("CharacterItems", items);
        }
        #endregion

        #region UpdateAbility
        /// <summary>
        /// Service method to update an ability record.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public ActionResult UpdateAbility(string name, int? id)
        {
            string itemUseType = Request.QueryString["usetype"];
            string itemName = Request.QueryString["itemname"];
            string itemHqid = Request.QueryString["hqid"];
            string itemDescription = Request.QueryString["description"];
            string itemCharges = Request.QueryString["charges"];
            string itemFrequency = Request.QueryString["frequency"];
            string itemIcon = Request.QueryString["icon"];

            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            if (character == null)
            {
                Response.StatusCode = 500;
                return PartialView("Message", "Unable to locate character or character invalid.  Please reload try again.");
            }

            if (string.IsNullOrEmpty(itemUseType))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item use type is invalid.");
            }

            ItemUseType type = ItemUseType.OnceEver;

            if (!Enum.TryParse(itemUseType, out type))
            {
                Response.StatusCode = 500;
                return PartialView("Message", "The item type is invalid.");
            }

            if (!string.IsNullOrEmpty(itemCharges))
            {
                int charges = 1;
                if (!int.TryParse(itemCharges, out charges))
                {
                    Response.StatusCode = 500;
                    return PartialView("Message", "The charges value must be a number.");
                }
            }
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            try
            {
                xHelper.UpdateAbility(id, type, itemHqid, itemName, itemDescription, itemCharges, itemFrequency, itemIcon);

                xHelper.SaveToCharacter(character); // save back xml changes.

                characterService.UpdateScratchPadCharacter(character); // set to modified.

                // Save changes to db.
                characterContext.Save();
            }
            catch (Exception exception)
            {
                Response.StatusCode = 500;
                return PartialView("Message", string.Format("A system error occured. {0}", exception.Message));
            }

            return PartialView("Message", "Item updated.");
        }
        #endregion

        #region RefreshSchools
        /// <summary>
        /// Service method to refresh the magical schools control view.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshSchools(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");                

            ICharacterService characterService = new CharacterService(characterContext);
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            // Get character by slug, user and session.
            SchoolAccessModel model = CreateSchoolAccessModel(context, xHelper);

            return PartialView("CharacterSchools", model);
        }
        #endregion

        #region RefreshSects
        /// <summary>
        /// Service method to refresh the power sects control view.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshSects(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            ICharacterService characterService = new CharacterService(characterContext);
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            // Get character by slug, user and session.
            SectAccessModel sectModel = CreateSectAccessModel(context, xHelper);

            return PartialView("CharacterSects", sectModel);
        }
        #endregion

        #region RefreshSpells
        /// <summary>
        /// Service method to refresh the list of character spells
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshSpells(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            var model = CreateSpellModel(context, xHelper);

            return PartialView("CharacterSpells", model);
        }
        #endregion

        #region RefreshInvocations
        /// <summary>
        /// Service method to refresh the list of character invocation
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public ActionResult RefreshInvocations(string name)
        {
            IUnitOfWork context = new HQContext();
            IUnitOfWork characterContext = new CharacterContext();
            ICharacterService characterService = new CharacterService(characterContext);

            if (!SetUserCredentials()) // invalid login credentials so log out user and send back to login page.
                return RedirectToAction("Index", "Home");

            // Get character by slug, user and session.
            ScratchPad character = characterService.GetScratchPadCharacter(userName, sessionId, name);

            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            var model = CreateInvocationModel(context, xHelper);

            return PartialView("CharacterInvocations", model);
        }
        #endregion
        
        #endregion

        #region CreateHeaderModel
        /// <summary>
        /// Helper method to create model data needed for the character header partial view.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="character"></param>
        /// <returns></returns>
        protected CharacterHeader CreateHeaderModel(IUnitOfWork context, ScratchPad character)
        {
            // Helper to help with getting values, totals etc. out of xml doc.
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            IRulesService rulesService = new RulesService(context);
            IGuildService guildService = new GuildService(context);

            int ruleSetId = rulesService.GetDefaultRules().RuleSetID;

            IEnumerable<Guild> guildList = 
                guildService.GetGuilds(ruleSetId)
                    .OrderBy(g => g.Name);

            IEnumerable<Guild> primaryGuilds =
                guildService.GetGuilds(ruleSetId, xHelper.Class())
                    .OrderBy(g => g.Name);

            IEnumerable<Guild> tertiaryGuilds = xHelper.GetAllowedTertiaryGuildList();

            string primaryGuild = xHelper.PrimaryGuild();
            int? primaryGuildId = null;

            if (!string.IsNullOrEmpty(primaryGuild))
            {
                Guild primary = guildService.GetGuild(ruleSetId, guildService.CreateGuildSlug(primaryGuild));

                if (primary != null)
                {
                    primaryGuildId = primary.GuildID;
                    guildList = guildService.GetAllowedGuilds(primary.GuildID);
                }
            }

            string secondaryGuild = xHelper.SecondaryGuild();
            int? secondaryGuildId = null;

            if (!string.IsNullOrEmpty(secondaryGuild))
            {
                Guild secondary = guildService.GetGuild(ruleSetId, guildService.CreateGuildSlug(secondaryGuild));

                if (secondary != null)
                    secondaryGuildId = secondary.GuildID;
            }

            string tertiaryGuild = xHelper.TertiaryGuild();
            int? tertiaryGuildId = null;

            if (!string.IsNullOrEmpty(tertiaryGuild))
            {
                Guild tertiary = guildService.GetGuild(ruleSetId, guildService.CreateGuildSlug(tertiaryGuild));

                if (tertiary != null)
                    tertiaryGuildId = tertiary.GuildID;
            }

            SelectList primaryGuildList = new SelectList(primaryGuilds, "GuildID", "Name", primaryGuildId);
            SelectList secondaryGuildList = new SelectList(guildList, "GuildID", "Name", secondaryGuildId);
            SelectList tertiaryGuildList = new SelectList((tertiaryGuilds != null) ? tertiaryGuilds : guildList, "GuildID", "Name", tertiaryGuildId);

            PointsTable table = rulesService.GetPointsTable(xHelper.Class(), xHelper.Race());

            CharacterHeader model = new CharacterHeader()
            {
                ID = character.ScratchPadID,
                Points = xHelper.PointsAvailable(), // total points.
                EffectivePoints = xHelper.EffectivePoints(),
                UnspentPoints = xHelper.PointsUnspent(),
                Status = xHelper.StatusAvailable(),
                UnspentStatus = xHelper.StatusUnspent(),
                Rank = xHelper.Rank(),
                Table = table,
                PrimaryGuilds = primaryGuildList,
                SecondaryGuilds = !string.IsNullOrEmpty(primaryGuild) ? secondaryGuildList : null,
                TertiaryGuilds = !string.IsNullOrEmpty(secondaryGuild) && xHelper.TertiaryGuildAllowed() ? tertiaryGuildList : null,
                Races = new SelectList(rulesService.GetRaces(ruleSetId), xHelper.Race()),
                Classes = new SelectList(rulesService.GetClasses(ruleSetId), xHelper.Class())
            };

            return model;
        }
        #endregion

        #region CreateAdventureRecordModel
        /// <summary>
        /// Create the model records for the character adventure record.
        /// </summary>
        /// <returns></returns>
        protected List<RecordItem> CreateAdventureRecordModel(IUnitOfWork context, ScratchPad character)
        {
            List<RecordItem> items = new List<RecordItem>();

            // Helper to help with getting values, totals etc. out of xml doc.
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            // get record items ordered by date.
            items = xHelper.GetAdventureRecords();

            return items;
        }
        #endregion

        #region CreateSilhouetteModel
        /// <summary>
        /// Create a view model for the character silhouette view.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="character"></param>
        /// <returns></returns>
        protected SilhouetteRecord CreateSilhouetteModel(IUnitOfWork context, ScratchPad character)
        {
            // Helper to help with getting values, totals etc. out of xml doc.
            CharacterRulesHelper xHelper = new CharacterRulesHelper(context, character.Xml);

            // Get life record.
            LifeRecord life = xHelper.GetLifeRecord();

            List<ACRecord> acs = xHelper.GetArmourClasses();

            SilhouetteRecord model = new SilhouetteRecord()
            {
                Life = life,
                AC = acs
            };

            return model;
        }
        #endregion

        #region CreateSchoolAccessModel
        /// <summary>
        /// Create a school access model.
        /// This encompasses a list of all the schools and the level of access available to the character
        /// plus a list of all the skills that grant a choice of guild (school).
        /// </summary>
        /// <param name="context"></param>
        /// <param name="helper"></param>
        /// <returns></returns>
        protected SchoolAccessModel CreateSchoolAccessModel(IUnitOfWork context, CharacterRulesHelper helper)
        {
            SchoolAccessModel model = new SchoolAccessModel();

            // Add existing school access.
            model.Records.AddRange(helper.GetSchoolAccess());

            // Add school status allocations.
            model.Status = helper.GetSpellsAllotment();

            // Add school choices from character.
            model.AccessItems.AddRange(helper.GetSchoolAccessOptions());

            // Add static list of schools of magic.
            string[] guildSlugs = new string[8]
                {
                    "black-school-of-magic",
                    "blue-school-of-magic",
                    "temple-of-earth-magic",
                    "green-school-of-magic",
                    "grey-school-of-magic",
                    "red-school-of-magic",
                    "white-school-of-magic",
                    "yellow-guild"
                };

            IRulesService rulesService = new RulesService(context);
            IGuildService guildService = new GuildService(context);

            var schools =
                guildService.GetGuilds(rulesService.GetDefaultRules().RuleSetID, guildSlugs)
                .Select(g => new SchoolItem { Slug = g.Slug, Name = g.Name })
                .ToList();

            model.Schools = schools;

            return model;
        }
        #endregion

        #region CreateSpellModel
        protected SpellListModel CreateSpellModel(IUnitOfWork context, CharacterRulesHelper helper)
        {
            SpellListModel model = new SpellListModel();

            // Get available spells from character.
            model.Spells.AddRange(helper.GetAvailableSpells());

            return model;
        }
        #endregion
        
        #region CreateInvocationModel
        protected InvocationListModel CreateInvocationModel(IUnitOfWork context, CharacterRulesHelper helper)
        {
            InvocationListModel model = new InvocationListModel();

            // Get available spells from character.
            model.Invocations.AddRange(helper.GetAvailableInvocations());

            return model;
        }
        #endregion

        #region CreateSectAccessModel
        /// <summary>
        /// Create a sect access model.
        /// This encompasses a list of all the sects and the level of access available to the character
        /// plus a list of all the skills that grant a choice of guild (sect).
        /// </summary>
        /// <param name="context"></param>
        /// <param name="helper"></param>
        /// <returns></returns>
        protected SectAccessModel CreateSectAccessModel(IUnitOfWork context, CharacterRulesHelper helper)
        {
            SectAccessModel model = new SectAccessModel();

            // Add existing sect access.
            model.Records.AddRange(helper.GetSectAccess());

            // Add school status allocations.
            model.Status = helper.GetSectStatus();

            // Add school choices from character.
            //model.AccessItems.AddRange(helper.GetSchoolAccessOptions());

            // Add static list of schools of magic.
            string[] guildSlugs = new string[12]
                {
                    "dark-brethren",
                    "dark-path",
                    "dark-seers",
                    "druids",
                    "grey-gauntlet",
                    "grey-path",
                    "grey-wardens",
                    "hospitallers",
                    "humacti",
                    "michelines",
                    "reapers",
                    "white-path"
                };

            IRulesService rulesService = new RulesService(context);
            IGuildService guildService = new GuildService(context);

            var sects =
                guildService.GetGuilds(rulesService.GetDefaultRules().RuleSetID, guildSlugs)
                .Select(g => new SectItem { Slug = g.Slug, Name = g.Name })
                .ToList();

            model.Sects = sects;

            return model;
        }
        #endregion

        #region CreateSkillList
        /// <summary>
        /// Create a list of skills for a drop down list.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected List<QualitySkillItem> CreateSkillList(IUnitOfWork context)
        {
            ISkillService skillService = new SkillService(context);
            IRulesService rulesService = new RulesService(context);

            int ruleSetId = rulesService.GetDefaultRules().RuleSetID;

            var skillList = (from a in skillService.GetRuleSkills(ruleSetId)
                             orderby a.Name
                             select new QualitySkillItem()
                             {
                                 Name = a.Name,
                                 Slug = a.Slug
                             }).ToList();

            return skillList;
        }
        #endregion
    
        #region CreateSectList
        /// <summary>
        /// Create a list of sects for a drop down list.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected List<QualitySectItem> CreateSectList(IUnitOfWork context)
        {
            IGuildService guildService = new GuildService(context);
            IRulesService rulesService = new RulesService(context);

            int ruleSetId = rulesService.GetDefaultRules().RuleSetID;

            var sectList = (from a in guildService.GetGuilds(ruleSetId)
                             where !string.IsNullOrEmpty(a.Sphere)
                             orderby a.Name
                             select new QualitySectItem()
                             {
                                 Name = a.Name,
                                 Slug = a.Slug
                             }).ToList();

            return sectList;
        }
        #endregion
    
        #region CreateSchoolList
        /// <summary>
        /// Create a list of schools for a drop down list.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected List<QualitySchoolItem> CreateSchoolList(IUnitOfWork context)
        {
            IGuildService guildService = new GuildService(context);
            IRulesService rulesService = new RulesService(context);

            int ruleSetId = rulesService.GetDefaultRules().RuleSetID;

            var schoolList = (from a in guildService.GetGuilds(ruleSetId)
                             where !string.IsNullOrEmpty(a.MagicColour)
                             orderby a.Name
                             select new QualitySchoolItem()
                             {
                                 Name = a.Name,
                                 Slug = a.Slug
                             }).ToList();

            return schoolList;
        }
        #endregion

        #region CreateIconList
        /// <summary>
        /// Create a list of available icons
        /// </summary>
        /// <returns></returns>
        protected System.Collections.Generic.List<HQPad.Models.IconItem> CreateIconList()
        {
            return CreateIconList(null);
        }

        /// <summary>
        /// Create a list of available icons
        /// </summary>
        /// <param name="folder">The name of an optional sub folder.</param>
        /// <returns></returns>
        protected System.Collections.Generic.List<HQPad.Models.IconItem> CreateIconList(string folder)
        {
            System.Collections.Generic.List<HQPad.Models.IconItem> icons = new List<IconItem>();

            string rootPath = "/Content/Images/icons/equipment/";

            if (!string.IsNullOrEmpty(folder))
                rootPath += folder.TrimStart('/').TrimEnd('/');

            string path = Server.MapPath(rootPath);

            if (!Directory.Exists(path))
                throw new ApplicationException(string.Format("Icon fodler {0} does not exist.", path));

            // get all png files.
            foreach (string file in Directory.EnumerateFiles(path, "*.png", SearchOption.TopDirectoryOnly))
            {
                icons.Add(new IconItem() { 
                    File = Path.GetFileName(file),
                    RelativePath = string.Format("{0}{1}", rootPath, Path.GetFileName(file))
                });
            }

            return icons;
        }
        #endregion

        #region SetUserCredentials
        /// <summary>
        /// Get the signed in user credentials and assign them to private values.
        /// </summary>
        /// <returns>Whether user credentials set successfully.  If not current user signed out.</returns>
        protected bool SetUserCredentials()
        {
            userName = AuthenticationHelper.GetCurrentUserName(HttpContext);
            sessionId = AuthenticationHelper.GetCurrentSessionId(Request);

            bool ok = true;

            if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(sessionId))
            {
                // invalid login credentials so log out user and send back to login page.
                AuthenticationHelper.Logout();
                ok = false;
            }

            return ok;
        }
        #endregion
    }
}
